﻿using System.Collections;
using System;

public abstract class ColEncapsulados
{
    #region Atributos de la clase

    protected ArrayList miColeccion;
    protected ArrayList miColeccionEliminados;

    #endregion

    #region Propiedades públicas

    public IEnumerable MiColeccion
    {
        get { return this.miColeccion; }
    }

    #endregion

    #region Constructores

    /**
        * \brief Constructor de ColEncapsulado.
        * \return La ColEncapsulado creado.
        */
    public ColEncapsulados()
    { 
        this.miColeccion = new ArrayList();
        this.miColeccionEliminados = new ArrayList();
    }

    #endregion

    #region Métodos de operación

    /**
    * \brief Función que comprueba si un encapsulado esta dentro de una colección.
    * \pre El encapsulado tiene que ser correcto.
    * \param pElemento - Encapsulado que se va a comprobar si se encuentra dentro de una colección.
    * \return TRUE si el elemento pertenece a la colección y FALSE si el elemento no pertenece a la colección.
    */
    protected bool existe(Encapsulado pElemento)
    {
        return this.miColeccion.Contains(pElemento);
    }
    /**
        * \brief Función que añade un encapsulado a una colección.
        * \pre El encapsulado tiene que ser correcto.
        * \param pElemento - Encapsulado que se va a añadir.
        */
    protected void añadir(Encapsulado pElemento)
    {
        if(pElemento != null && !this.existe(pElemento))
            this.miColeccion.Add(pElemento);
    }
    /**
        * \brief Función que añade una colección a la colección.
        * \pre La colección tiene que ser correcta.
        * \param pColeccion - colección que se va a añadir.
        */
    protected void añadirColeccion(ColEncapsulados pColeccion)
    {
        foreach (Encapsulado elemento in pColeccion)
            this.añadir(elemento);
    }
    /**
        * \brief Función que elimina un encapsulado que esta dentro de una colección
        * \pre El encapsulado tiene que ser correcto.
        * \param pElemento - Encapsulado que se va a eliminar.
        */
    protected void eliminar(Encapsulado pElemento)
    {
        this.miColeccion.Remove(pElemento);

        if (pElemento != null && !this.miColeccionEliminados.Contains(pElemento))
            this.miColeccionEliminados.Add(pElemento);
    }
    /**
    * \brief Función que elimina una colección.
    * \pre La colección tiene que ser correcta.
    * \param pColeccion - colección que se va a eliminar.
    */
    protected void eliminarColeccion(ColEncapsulados pColeccion)
    {
        foreach (Encapsulado elemento in pColeccion)
            this.eliminar(elemento);
    }
        
    public void vaciarColeccion()
    {
        this.miColeccionEliminados.AddRange(this.miColeccion);
        this.miColeccion.Clear();
    }

    #endregion

    #region Métodos de consulta

    /**
    * \brief Obtiene el encapsulado que se encuentra en la posición indicada.
    * \param pIndice - Posición de la colección que queremos consultar.
    * \return Devuelve el Encapsulado que se encuentra en la posición indicada.
    */
    public virtual Encapsulado getEncapsuladoByIndice(int pIndice)
    {
        if (pIndice < 0 || pIndice > (this.Tamaño - 1))
            return null;
        return (Encapsulado)this.miColeccion[pIndice];
    }
    /**
        * \brief Obtiene un diccionario por su Id
        * \pre El Id de diccionario debe ser correcto
        * \param pId - Identificador del diccionario que queremos obtener
        * \return Un encapsulado Diccionario
        */
    protected virtual Encapsulado getEncapsuladoById(ulong pId)
    {
        Encapsulado encapsulado = null;

        foreach (Encapsulado e in this.miColeccion)
        {
            if (e.Id.Equals(pId))
            {
                encapsulado = e;
                break;
            }
        }
        return encapsulado;
    }

    #endregion

    #region Métodos adicionales

    /**
        * \brief Función que comprueba si una colección está vacía.
        * \return TRUE si la colección está vacía y FALSE en el otro caso.
        */
    virtual public bool coleccionVacia()
    {
        if (this.miColeccion.Count == 0)
            return true;
        else
            return false;           
    }
    /**
        * \brief Función que devuelve el enumerador de la coleccion.
        * \return IEnunerator de la colección.
        */
    virtual public IEnumerator GetEnumerator()
    {
        return this.miColeccion.GetEnumerator();
    }
    /**
        * \brief Función que devuelve el número de elementos de la coleccion.
        * \return Entero correspondiente al tamaño de la colección.
        */
    virtual public int Tamaño
    {
        get { return this.miColeccion.Count; }
    }

    public override bool Equals(object obj)
    {
        if (this.GetType().Equals(obj.GetType()))
        {
            ColEncapsulados coleccionAComparar = (ColEncapsulados)obj;

            //Si tienen distinto tamaño automaticamente son distintas
            if (this.Tamaño == coleccionAComparar.Tamaño)
            {
                foreach (Encapsulado e in this.miColeccion)
                    if (!coleccionAComparar.existe(e))
                        return false;
            }
            else
                return false;

            return true;
        }
        else
            throw new Exception("Error: Método Equals; Clase ColEncapsulados; No se pueden comparar dos objetos distintos");
    }

    public override int GetHashCode()
    {
        if (this.Tamaño == 0)
            return 0;
        else
        {
            //Definimos un numero entero primo
            int primo = 31;
            int resultado = 1;
            foreach (Encapsulado e in this.miColeccion)
            {
                resultado = primo * resultado + ((e == null) ? 0 : e.GetHashCode());
            }

            return resultado;
        }
    }

    internal abstract ColEncapsulados dameCopiaColeccionEncapsulados();

    internal virtual void restaurarColeccionEncapsulados(ColEncapsulados pColEncapsulado)
    {
        ArrayList elementoAEliminar = new ArrayList();
        foreach (Encapsulado e in this.miColeccion)
        {   
            Encapsulado eCopia = pColEncapsulado.getEncapsuladoById(e.Id);
            if (eCopia != null) e.restaurarEncapsulado(eCopia);
            else elementoAEliminar.Add(e);
        }
        foreach (Encapsulado e in elementoAEliminar)
            this.miColeccion.Remove(e);

        elementoAEliminar.Clear();
        foreach (Encapsulado e in this.miColeccionEliminados)
        {
            Encapsulado eCopia = pColEncapsulado.getEncapsuladoById(e.Id);
            if (eCopia != null)
            {
                e.restaurarEncapsulado(eCopia);
                elementoAEliminar.Add(e);
                this.miColeccion.Add(e);
            }
        }
        foreach (Encapsulado e in elementoAEliminar)
            this.miColeccionEliminados.Remove(e);
    }

    #endregion
}